import { _decorator, Component } from 'cc'
import BTComposite from './Base/BTComposite'
import BTConditional from './Base/BTConditional'
import BTNode from './Base/BTNode'
import BTParent from './Base/BTParent'
import BTTree from './Base/BTTree'
import MyTree from './Biz/Tree/MyTree'
import { AbortType, NodeStatus } from './Enum'
const { ccclass } = _decorator

@ccclass('BehaviorManager')
export class BehaviorManager extends Component {
  restartWhenComplete = false // 是否在完成时重启
  tree: BTTree // 行为树
  nodeList: BTNode[] = [] // 存放行为树的所有节点,使用树的前序遍历（根节点 -> 左子树 -> 右子树）
  activeStack: number[][] = [] // 当前正在执行的节点栈
  parentIndex: number[] = [] // 存放每个节点的父节点索引
  childrenIndex: number[][] = [] // 存放每个节点的子节点索引

  relativeChildIndex: number[] = [] // 当前节点在父节点中的索引
  parentCompositeIndex: number[] = [] // 当前节点的父亲节点的索引
  childContionalIndex: number[][] = [] // 孩子是条件节点的索引数组

  conditionalReevaluates: ConditionalReevaluate[] = []
  conditionalReevaluateMap: Map<number, ConditionalReevaluate> = new Map()

  start() {
    this.enableBehavior()
    // this.scheduleOnce(() => {
    //   console.log('加血')
    //   BlackBoard.Instance.hp = 100
    // }, 1)
  }

  restart() {
    console.log('重启行为树')
    this.removeChildConditionalReevaluate(-1)
    this.pushNode(0, 0)
  }

  enableBehavior() {
    this.tree = new MyTree()
    this.activeStack.push([])
    // 根节点的parentIndex为-1
    this.parentIndex.push(-1)
    this.relativeChildIndex.push(-1)
    this.parentCompositeIndex.push(-1)
    this.addToNodeList(this.tree.root, { parentCompositeIndex: -1 })
    // 将根节点的索引加入到activeStack中
    this.pushNode(0, 0)
  }

  addToNodeList(node: BTNode, data: { parentCompositeIndex: number }) {
    this.nodeList.push(node)
    // 当前节点在nodeList中的索引
    const index = this.nodeList.length - 1
    // 如果是父节点,则需要将子节点加入到nodeList中
    if (node instanceof BTParent) {
      this.childrenIndex.push([])
      this.childContionalIndex.push([])
      for (let i = 0; i < node.childrens.length; i++) {
        this.relativeChildIndex.push(i)
        this.parentIndex.push(index)
        this.childrenIndex[index].push(this.nodeList.length)
        // 如果当前节点是组合节点,则需要将父组合节点的索引加入到data中
        if (node instanceof BTComposite) {
          data.parentCompositeIndex = index
        }
        this.parentCompositeIndex.push(data.parentCompositeIndex)
        this.addToNodeList(node.childrens[i], data)
      }
    } else {
      this.childrenIndex.push(null)
      this.childContionalIndex.push(null)
      if (node instanceof BTConditional) {
        const parentCompositeIndex = this.parentCompositeIndex[index]
        if (parentCompositeIndex !== -1) {
          this.childContionalIndex[parentCompositeIndex].push(index)
        }
      }
    }
  }

  update(_dt: number) {
    this.tick()
  }

  tick() {
    this.reevaluateConditionalNode()

    for (let i = this.activeStack.length - 1; i >= 0; i--) {
      const stack = this.activeStack[i]
      let prevIndex = -1
      let prevStatus = NodeStatus.Inactive
      while (prevStatus !== NodeStatus.Running && i < this.activeStack.length && stack.length) {
        const curIndex = stack[stack.length - 1]
        if (curIndex === prevIndex) {
          break
        }
        prevIndex = curIndex
        prevStatus = this.runNode(curIndex, i, prevStatus)
      }
    }
  }

  pushNode(index: number, stackIndex: number) {
    const stack = this.activeStack[stackIndex]
    if (stack.length === 0 || stack[stack.length - 1] !== index) {
      stack.push(index)
      const node = this.nodeList[index]
      console.log('pushNode:', node)
      node.onStart()
    }
  }

  popNode(index: number, stackIndex: number, status: NodeStatus, popChildren = true): NodeStatus {
    const stack = this.activeStack[stackIndex]
    stack.pop()
    const node = this.nodeList[index]
    node.onEnd()
    console.log('popNode:', node)
    const parentIndex = this.parentIndex[index]
    if (parentIndex !== -1) {
      // 如果当前节点是条件节点,则需要根据条件节点的abortType来决定是否需要中止
      if (node instanceof BTConditional) {
        const parentCompositeIndex = this.parentCompositeIndex[index]
        if (parentCompositeIndex !== -1) {
          const compositeNode = this.nodeList[parentCompositeIndex] as BTComposite
          if (compositeNode.abortType !== AbortType.None) {
            if (this.conditionalReevaluateMap.has(index)) {
              const conditionalReevaluate = this.conditionalReevaluateMap.get(index)
              conditionalReevaluate.compositeIndex = -1
              conditionalReevaluate.status = status
            } else {
              const conditionalReevaluate = new ConditionalReevaluate(
                index,
                status,
                compositeNode.abortType === AbortType.LowPriority ? -1 : parentCompositeIndex
              )
              console.log('生成conditionalReevaluate:', conditionalReevaluate)

              this.conditionalReevaluates.push(conditionalReevaluate)
              this.conditionalReevaluateMap.set(index, conditionalReevaluate)
            }
          }
        }
      }
      const parentNode = this.nodeList[parentIndex] as BTParent
      if (node instanceof BTParent) {
        status = node.decorate(status)
      }
      parentNode.onChildExecuted(status, this.relativeChildIndex[index])
    }

    // 如果当前节点是组合节点
    if (node instanceof BTComposite) {
      if (node.abortType === AbortType.Self || node.abortType === AbortType.None || !stack.length) {
        this.removeChildConditionalReevaluate(index)
      } else if (node.abortType === AbortType.LowPriority || node.abortType === AbortType.Both) {
        for (let i = 0; i < this.childContionalIndex[index].length; i++) {
          const childContionalIndex = this.childContionalIndex[index][i]
          if (this.conditionalReevaluateMap.has(childContionalIndex)) {
            const conditionalReevaluate = this.conditionalReevaluateMap.get(childContionalIndex)
            conditionalReevaluate.compositeIndex = this.parentCompositeIndex[index]
          }
        }

        for (let i = 0; i < this.conditionalReevaluates.length; i++) {
          const conditionalReevaluate = this.conditionalReevaluates[i]
          if (conditionalReevaluate.compositeIndex === index) {
            conditionalReevaluate.compositeIndex = this.parentCompositeIndex[index]
          }
        }
      }
    }

    if (popChildren) {
      for (let i = this.activeStack.length - 1; i > stackIndex; i--) {
        const stack = this.activeStack[i]
        if (stack.length >= 0 && this.isParentNode(index, stack[stack.length - 1])) {
          for (let j = stack.length - 1; j >= 0; j--) {
            this.popNode(stack[stack.length - 1], i, NodeStatus.Failure, false)
          }
        }
      }
    }

    if (stack.length === 0) {
      if (stackIndex === 0) {
        if (this.restartWhenComplete) {
          this.restart()
        }
      } else {
        this.activeStack.splice(stackIndex, 1)
      }
    }

    return status
  }

  runNode(index: number, stackIndex: number, prevStatus: NodeStatus) {
    this.pushNode(index, stackIndex)
    const node = this.nodeList[index]
    let status = prevStatus
    if (node instanceof BTParent) {
      status = this.runParentNode(index, stackIndex, status)
      if (node.canRunParallelChildren()) {
        status = node.status
      }
    } else {
      status = node.onUpdate()
    }

    if (status !== NodeStatus.Running) {
      status = this.popNode(index, stackIndex, status)
    }
    return status
  }

  runParentNode(index: number, stackIndex: number, status: NodeStatus): NodeStatus {
    const node = this.nodeList[index] as BTParent
    if (!node.canRunParallelChildren() || node.status !== NodeStatus.Running) {
      let childStatus = NodeStatus.Inactive
      while (node.canExecute() && (childStatus !== NodeStatus.Running || node.canRunParallelChildren())) {
        const childIndex = node.index
        if (node.canRunParallelChildren()) {
          this.activeStack.push([])
          stackIndex = this.activeStack.length - 1
          node.onChildStart()
        }
        childStatus = status = this.runNode(this.childrenIndex[index][childIndex], stackIndex, status)
      }
    }

    return status
  }

  removeChildConditionalReevaluate(index: number) {
    for (let i = this.conditionalReevaluates.length - 1; i >= 0; i--) {
      const conditionalReevaluate = this.conditionalReevaluates[i]
      if (conditionalReevaluate.compositeIndex === index) {
        console.log('移除conditionalReevaluate:', conditionalReevaluate)
        this.conditionalReevaluateMap.delete(conditionalReevaluate.index)
        this.conditionalReevaluates.splice(i, 1)
      }
    }
  }

  reevaluateConditionalNode() {
    for (let i = this.conditionalReevaluates.length - 1; i >= 0; i--) {
      const { index, status: prevStatus, compositeIndex } = this.conditionalReevaluates[i]
      if (compositeIndex === -1) {
        continue
      }
      const status = this.nodeList[index].onUpdate()
      if (status === prevStatus) {
        continue
      }

      for (let j = this.activeStack.length - 1; j >= 0; j--) {
        const stack = this.activeStack[j]
        let curNodeIndex = stack[stack.length - 1]
        const commonParentIndex = this.findCommonParentIndex(curNodeIndex, index)
        if (this.isParentNode(compositeIndex, commonParentIndex)) {
          const stackLen = this.activeStack.length
          while (curNodeIndex !== -1 && commonParentIndex !== curNodeIndex && stackLen === this.activeStack.length) {
            this.popNode(curNodeIndex, j, NodeStatus.Failure, false)
            curNodeIndex = this.parentIndex[curNodeIndex]
          }
        }
      }

      // 重评估成功的条件节点右侧（包括自己）的条件重评估对象移除
      for (let j = this.conditionalReevaluates.length - 1; j >= i; j--) {
        const conditionalReevaluate = this.conditionalReevaluates[j]
        if (this.isParentNode(compositeIndex, conditionalReevaluate.index)) {
          this.conditionalReevaluateMap.delete(conditionalReevaluate.index)
          this.conditionalReevaluates.splice(j, 1)
        }
      }

      const compositeNode = this.nodeList[this.parentCompositeIndex[index]] as BTComposite
      for (let j = i - 1; j >= 0; j--) {
        const conditionalReevaluate = this.conditionalReevaluates[j]
        if (this.parentCompositeIndex[conditionalReevaluate.index] === this.parentCompositeIndex[index]) {
          if (compositeNode.abortType === AbortType.LowPriority) {
            conditionalReevaluate.compositeIndex = -1
          }
        }
      }

      const conditionalParentIndex = []
      for (let j = this.parentIndex[index]; j !== compositeIndex; j = this.parentIndex[j]) {
        conditionalParentIndex.push(j)
      }
      conditionalParentIndex.push(compositeIndex)

      for (let j = conditionalParentIndex.length - 1; j >= 0; j--) {
        const parentNode = this.nodeList[conditionalParentIndex[j]] as BTParent
        if (j === 0) {
          parentNode.onConditionalAbort(this.relativeChildIndex[index])
        } else {
          parentNode.onConditionalAbort(this.relativeChildIndex[conditionalParentIndex[j - 1]])
        }
      }
    }
  }

  findCommonParentIndex(curNodeIndex: number, index: number) {
    const set = new Set()
    let num = curNodeIndex
    while (num !== -1) {
      set.add(num)
      num = this.parentIndex[num]
    }
    num = index
    while (!set.has(num)) {
      num = this.parentIndex[num]
    }

    return num
  }

  isParentNode(parentIndex: number, childIndex: number) {
    for (let i = childIndex; i !== -1; i = this.parentIndex[i]) {
      if (i === parentIndex) {
        return true
      }
    }
    return false
  }
}

class ConditionalReevaluate {
  constructor(
    public index: number,
    public status: NodeStatus,
    public compositeIndex: number
  ) {}
}
